//
//  NSObject+NSObjectExtend.m
//  etionMisc
//
//  Created by wu jingxing on 13-5-8.
//  Copyright (c) 2013年 GuangZhouXuanWu. All rights reserved.
//

#import "NSObjectExtend.h"

@implementation NSObject (NSObjectProperty)

+ (NSString *)getProperty:(Class)cls name:(NSString *)szName
{
    objc_property_t property = class_getProperty(cls, [szName UTF8String]);
    if (property == NULL)
        return nil;
    const char *atts = property_getAttributes(property);
    if (atts == NULL)
        return nil;
    return [NSString stringWithUTF8String:atts];
}

+ (NSString *)getPropertyType:(Class)cls property:(NSString *)szProperty
{
    return [NSObject getPropertyType:cls property:szProperty isKindOfNSObject:NULL];
}

+ (NSString *)getPropertyType:(Class)cls property:(NSString *)szProperty isKindOfNSObject:(BOOL*)isKindOf
{
    return [NSObject _getPropertyType:[self getProperty:cls name:szProperty] isKindOfNSObject:isKindOf];
}

+ (NSString *)_getPropertyType:(NSString *)szProperty isKindOfNSObject:(BOOL*)isKindOf
{
    NSString *szType = @"";
    NSMutableCharacterSet *set = [[NSMutableCharacterSet new] autorelease];
    [set addCharactersInString:@"T"];
    [set addCharactersInString:@","];
    NSArray *ar = [szProperty componentsSeparatedByCharactersInSet:set];
    if (ar.count > 1)
    {
        NSString *s = [ar objectAtIndex:1];
        if ([[s substringToIndex:1] isEqualToString:@"@"] == YES)
        {
            if(isKindOf!=NULL)
                *isKindOf = YES;
            set = [[NSMutableCharacterSet new] autorelease];
            [set addCharactersInString:@"@\""];
            [set addCharactersInString:@"\""];
            NSArray *ar = [szProperty componentsSeparatedByCharactersInSet:set];
            if (ar.count > 1)
                s = [ar objectAtIndex:2];
        }
        szType = s;
    }
    return szType;
}

- (NSString *)getPropertyType:(NSString *)szProperty
{
    return [self getPropertyType:szProperty isKindOfNSObject:NULL];
}

- (NSString *)getPropertyType:(NSString *)szProperty isKindOfNSObject:(BOOL*)isKindOf
{
    return [NSObject getPropertyType:[self class] property:szProperty isKindOfNSObject:isKindOf];
}

- (NSArray *)getPropertyNameList
{
    return [NSObject getPropertyNameList:[self class]];
}

+ (NSArray *)getPropertyNameList:(Class)cls
{
    unsigned int outCount = 0;
    objc_property_t *propertyList = class_copyPropertyList(cls, &outCount);
    if (propertyList == NULL || outCount == 0)
        return nil;
    NSMutableArray *ar = [[[NSMutableArray alloc]initWithCapacity:outCount]autorelease_mm];
    for(unsigned int n = 0;n < outCount; n++)
    {
        const char *name = property_getName(propertyList[n]);
        if (name == NULL)
            continue;
        [ar addObject:[NSString stringWithUTF8String:name]];
    }
    return ar;
}

+ (ENSObjectPropertyType)NSObjectPropertyType:(NSString *)szProperty
{
    if ([NSObject isNSStringType:szProperty])
        return ENSObjectPropertyTypeNSString;
    if ([NSObject isNSDateType:szProperty])
        return ENSObjectPropertyTypeNSDate;
    if ([NSObject isNSNumberType:szProperty])
        return ENSObjectPropertyTypeNSNumber;

    if ([NSObject isNSDataType:szProperty])
        return ENSObjectPropertyTypeNSData;


    if ([NSObject isNumberIntType:szProperty])
        return ENSObjectPropertyTypeNumberInt;
    if ([NSObject isNumberLongLongType:szProperty])
        return ENSObjectPropertyTypeNumberLongLong;
    if ([NSObject isNumberULongLongType:szProperty])
        return ENSObjectPropertyTypeNumberULongLong;
    if ([NSObject isNumberFloatType:szProperty])
        return ENSObjectPropertyTypeNumberFloat;
    if ([NSObject isNumberDoubleType:szProperty])
        return ENSObjectPropertyTypeNumberDouble;
    
    if ([NSObject isNSArrayType:szProperty])
        return ENSObjectPropertyTypeArray;
    if ([NSObject isNSDictionaryType:szProperty])
        return ENSObjectPropertyTypeDictionary;
    
    if ([NSObject isNSMutableArrayType:szProperty])
        return ENSObjectPropertyTypeArray;
    if ([NSObject isNSMutableDictionaryType:szProperty])
        return ENSObjectPropertyTypeDictionary;


    if ([NSObject isIntType:szProperty])
        return ENSObjectPropertyTypeInt;
    if ([NSObject isFloatType:szProperty])
        return ENSObjectPropertyTypeFloat;
    if ([NSObject isDoubleType:szProperty])
        return ENSObjectPropertyTypeDouble;

    if ([NSObject isBoolType:szProperty])
        return ENSObjectPropertyTypeBOOL;
    
    return ENSObjectPropertyTypeID;
}

+ (ENSObjectPropertyType)NSNumberType:(NSNumber *)number
{
    if ([NSObject isNSNumberIntType:number] == YES)
        return ENSObjectPropertyTypeNSNumberInt;
    if ([NSObject isNSNumberLongLongType:number] == YES)
        return ENSObjectPropertyTypeNSNumberLongLong;
    if ([NSObject isNSNumberDoubleType:number] == YES)
        return ENSObjectPropertyTypeNSNumberDouble;
    if ([NSObject isNSNumberFloatType:number] == YES)
        return ENSObjectPropertyTypeNSNumberFloat;
    return ENSObjectPropertyTypeNSNumber;
}

+ (BOOL)isBoolType:(NSString *)szType
{
    return [szType isEqualToString:@"B"];
}

+ (BOOL)isNSStringType:(NSString *)szType
{
    return [szType isEqualToString:@"NSString"];
}

+ (BOOL)isNSDateType:(NSString *)szType
{
    return [szType isEqualToString:@"NSDate"];
}

+ (BOOL)isNSDataType:(NSString *)szType
{
    return [szType isEqualToString:@"NSData"];
}

+ (BOOL)isNSNumberType:(NSString *)szType
{
    return [szType isEqualToString:@"NSNumber"];
}

+ (BOOL)isNSXDictionaryType:(NSString *)szType
{
    return [NSObject isNSDictionaryType:szType] || [NSObject isNSMutableDictionaryType:szType];
}

+ (BOOL)isNSDictionaryType:(NSString *)szType
{
    return [szType isEqualToString:@"NSDictionary"];
}

+ (BOOL)isNSMutableDictionaryType:(NSString *)szType
{
    return [szType isEqualToString:@"NSMutableDictionary"];
}

+ (BOOL)isNSXArrayType:(NSString *)szType
{
    return [NSObject isNSArrayType:szType] || [NSObject isNSMutableArrayType:szType];
}

+ (BOOL)isNSArrayType:(NSString *)szType
{
    return [szType isEqualToString:@"NSArray"];
}

+ (BOOL)isNSMutableArrayType:(NSString *)szType
{
    return [szType isEqualToString:@"NSMutableArray"];
}

+ (BOOL)isCGUIDype:(NSString *)szType
{
    return [szType isEqualToString:@"XMSGUID"];
}

+ (BOOL)isNSNumberIntType:(NSString *)szType number:(NSNumber *)number
{
    if ([NSObject isNSNumberType:szType] == YES)
    {
        return [NSObject isNSNumberIntType:number];
    }
    return NO;
}

+ (BOOL)isNSNumberBOOLType:(NSNumber *)number
{
    if (strcasecmp(number.objCType, @encode(Boolean)) == 0)
        return YES;
    return NO;
}

+ (BOOL)isNSNumberIntType:(NSNumber *)number
{
    if (strcmp(number.objCType, @encode(int)) == 0)
        return YES;
    return NO;
}

+ (BOOL)isNSNumberLongLongType:(NSNumber *)number
{
    if (strcmp(number.objCType, @encode(long long)) == 0)
        return YES;
    return NO;
}

+ (BOOL)isNSNumberFloatType:(NSNumber *)number
{
    if (strcmp(number.objCType, @encode(float)) == 0)
        return YES;
    return NO;
}

+ (BOOL)isNSNumberDoubleType:(NSNumber *)number
{
    if (strcmp(number.objCType, @encode(double)) == 0)
        return YES;
    return NO;
}




+ (BOOL)isNumberIntType:(NSString *)szType
{
    return [szType isEqualToString:@"XMSNumberInt"];
}

+ (BOOL)isNumberLongLongType:(NSString *)szType
{
    return [szType isEqualToString:@"XMSNumberLongLong"];
}

+ (BOOL)isNumberULongLongType:(NSString *)szType
{
    return [szType isEqualToString:@"XMSNumberULongLong"];
}

+ (BOOL)isNumberFloatType:(NSString *)szType
{
    return [szType isEqualToString:@"XMSNumberFloat"];
}

+ (BOOL)isNumberDoubleType:(NSString *)szType
{
    return [szType isEqualToString:@"XMSNumberDouble"];
}


+ (BOOL)isIntType:(NSString *)szType
{
    return [[szType lowercaseString] isEqualToString:@"i"];
}

+ (BOOL)isFloatType:(NSString *)szType
{
    return [[szType lowercaseString] isEqualToString:@"f"];
}

+ (BOOL)isDoubleType:(NSString *)szType
{
    return [[szType lowercaseString] isEqualToString:@"d"];
}

+ (BOOL)isGUIDType:(NSString *)szType
{
    return [szType rangeOfString:@"_GUID"].location!=NSNotFound;
}

+ (BOOL)isIntPointType:(NSString *)szType
{
    return [[szType lowercaseString] isEqualToString:@"^i"];
}

+ (BOOL)isFloatPointType:(NSString *)szType
{
    return [[szType lowercaseString] isEqualToString:@"^f"];
}

+ (BOOL)isDoublePointType:(NSString *)szType
{
    return [[szType lowercaseString] isEqualToString:@"^d"];
}

+ (NSArray*)_objectArrayToObjectKeyValue:(NSArray*)arObject
{
    NSMutableArray *ar=[[NSMutableArray new]autorelease_mm];
    for(NSObject *i in arObject)
    {
        if([i isKindOfClass:[NSString class]]||[i isKindOfClass:[NSValue class]]||[i isKindOfClass:[NSDate class]]||[i isKindOfClass:[NSData class]]||[i isKindOfClass:[XMSGUID class]])
        {
            [ar addObject:i];
        }
        else if([i isKindOfClass:[NSArray class]])
        {
            [ar addObject:[NSObject _objectArrayToObjectKeyValue:(NSArray*)i]];
        }
        else if([i isKindOfClass:[NSDictionary class]])
        {
            [ar addObject:i];
        }
        else
        {
            [ar addObject:[i objectPropertyToDictionaryKeyValue]];
        }
    }
    return ar;
}

- (NSDictionary*)objectPropertyToDictionaryKeyValue
{
    NSMutableDictionary *dic=[[NSMutableDictionary new] autorelease_mm];
    NSArray *arProperty=[self getPropertyNameList];
    for(NSString *key in arProperty)
    {
        BOOL isKindOf=NO;
        NSString *p=[self getPropertyType:key isKindOfNSObject:&isKindOf];
        if(isKindOf==YES)
        {
            if ([NSObject isNSStringType:p]||[NSObject isNSNumberType:p]||[NSObject isNSDateType:p]||[NSObject isNSDataType:p]||[NSObject isCGUIDype:p])
            {
                id value=[self valueForKey:key];
                [dic setObject:value!=nil?value:[NSNull null] forKey:key];
            }
            else if([NSObject isNSXArrayType:p])
            {
                NSArray *ar=[NSObject _objectArrayToObjectKeyValue:[self valueForKey:key]];
                [dic setObject:ar forKey:key];
            }
            else if([NSObject isNSXDictionaryType:p])
            {
                [dic setObject:[self valueForKey:key] forKey:key];
            }
            else
            {
                [dic setObject:[[self valueForKey:key] objectPropertyToDictionaryKeyValue] forKey:key];
            }
        }
        else
        {
            if([NSObject isGUIDType:p])
            {
                GUID guid={0};
                memcpy(&guid,[self valueForKey:key],sizeof(GUID));
                [dic setObject:[[[XMSGUID alloc]initWithGUID:guid]autorelease_mm] forKey:key];
            }
            else if([NSObject isIntType:p]||[NSObject isFloatType:p]||[NSObject isDoubleType:p])
            {
                [dic setObject:[self valueForKey:key] forKey:key];
            }
        }
    }
    return dic;
}

- (NSString *)descriptionWithSpace:(NSString *)space
{
    NSString *description = [[self description] stringByReplacingOccurrencesOfString:@"{" withString:@"\n{"];
    return [description stringByReplacingOccurrencesOfString:@"\n" withString:[NSString stringWithFormat:@"\n%@", space]];
}

@end

@implementation NSObject (NSObjectSendMessage)

+ (void *)objcMsgSendSuper:(id)receiver class:(Class)cls selector:(SEL)op arguments:(id)firstArgument, ...
{
    struct objc_super sp={receiver,cls};
//    return objc_msgSendSuper(&sp, op, firstArgument);
    id(*action)(struct objc_super *super, SEL op, id firstArgument, ...) = (id(*)(struct objc_super *super, SEL op, id firstArgument, ...))objc_msgSendSuper;
    return action(&sp, op, firstArgument);
}

- (void *)objcMsgSendSuper:(Class)cls selector:(SEL)op arguments:(id)firstArgument, ...
{
    return [NSObject objcMsgSendSuper:self class:cls selector:op arguments:firstArgument, nil];
}

@end

@implementation NSObject (NSObjectAssociatedObject)

-(void)setAssociatedAssingValue:(id)value withKey:(const char*)key
{
    [self setAssociatedValue:value withKey:key andPolicy:OBJC_ASSOCIATION_ASSIGN];
}

-(void)setAssociatedRetainNonatomicValue:(id)value withKey:(const char*)key
{
    [self setAssociatedValue:value withKey:key andPolicy:OBJC_ASSOCIATION_RETAIN_NONATOMIC];
}

-(void)setAssociatedValue:(id)value withKey:(const char *)key andPolicy:(objc_AssociationPolicy)policy
{
    objc_setAssociatedObject(self, key, value, policy);
}

-(id)associatedValueForKey:(const char *)key
{
    return objc_getAssociatedObject(self, key);
}

-(void)removeAssociatedValues
{
    objc_removeAssociatedObjects(self);
}

@end

@implementation NSObject (NSObjectNotification)

- (void)addMainThreadObserverWithDictionarySelector:(NSDictionary*)dicSelector
{
    [self addMainThreadObserverWithDictionarySelector:dicSelector object:nil];
}

- (void)addMainThreadObserverWithDictionarySelector:(NSDictionary*)dicSelector object:(id)object
{
    NSDictionary *old = [self associatedValueForKey:"mainThreadObserver"];
    if(old.count > 0)
    {
        [dicSelector enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
    {
            if([old objectForKey:key] != nil)
               [NSNotificationCenter removeObserver:[old valueForKey:key]];
    }];
    }
    
    NSDictionary *dic = [NSNotificationCenter addMainThreadObserver:self dictionarySelector:dicSelector object:object];
    NSMutableDictionary *dicT = [NSMutableDictionary dictionaryWithDictionary:old];
    [dicT addEntriesFromDictionary:dic];
    
    [self setAssociatedRetainNonatomicValue:dicT withKey:"mainThreadObserver"];
}

- (void)removeObserver
{
    [(NSDictionary*)[self associatedValueForKey:"mainThreadObserver"] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
    {
        [NSNotificationCenter removeObserver:obj];
    }];
    [self removeAssociatedValues];
    [NSNotificationCenter removeObserver:self];
}

@end
